﻿using System;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using System.Collections.Generic;
using Microsoft.Practices.Unity.InterceptionExtension;
using Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

namespace SGRapidForm.Extensions
{
    public static class Extension
    {
        public static IList<UnityTypeMapping> Copy(this UnityTypeElementCollection unityTypeElements)
        {
           IList<UnityTypeMapping> mappings = new List<UnityTypeMapping>();
           foreach (UnityTypeElement type in unityTypeElements)
           {
               mappings.Add(new UnityTypeMapping { Type = type.Type, MapTo = type.MapTo, Name = type.Name });
           }

           return mappings;            
        }
    }

    public class UnityTypeMapping
    {
        public Type Type
        { get; set; }

        public Type MapTo
        { get; set; }

        public string Name
        { get; set; }
    }
   

    public class UnityInstanceProvider: IInstanceProvider
    {
        private static readonly TransparentProxyInterceptor injector = new TransparentProxyInterceptor();
        private static object syncHelper = new object();
        private Type _contractType;

        public static IDictionary<string, IUnityContainer> Containers
        { get; private set; }

        private static IDictionary<string, IList<UnityTypeMapping>> ContainerElementSettings
        { get;  set; } 

        private IUnityContainer Container
        { get;  set; }

        private IList<UnityTypeMapping> ContainerElementSetting
        { get;  set; }

        static UnityInstanceProvider()
        {
            ContainerElementSettings = new Dictionary<string, IList<UnityTypeMapping>>();
            Containers = new Dictionary<string, IUnityContainer>();
        }

        public UnityInstanceProvider(Type contractType, string containerName)
        {
            if (contractType == null)
            {
                throw new ArgumentNullException("contractType");
            }

            this._contractType = contractType;

            string key = containerName ?? "";
            if (Containers.ContainsKey(key))
            {
                this.Container = Containers[key];
                this.ContainerElementSetting = ContainerElementSettings[key];
                return;
            }
            UnityContainerElement containerElement = this.GetUnitySettings(containerName);
            IUnityContainer container = new UnityContainer();
            containerElement.Configure(container);
            container.AddNewExtension<Interception>();
            PolicyInjectionSettings section = (PolicyInjectionSettings)ConfigurationSourceFactory.Create().GetSection("policyInjection");
            if (section != null)
            {
                section.ConfigureContainer(this.Container, ConfigurationSourceFactory.Create());
            }
            lock (syncHelper)
            {
                if (!Containers.ContainsKey(key))
                {
                    Containers[key] = container;
                    ContainerElementSettings[key] = containerElement.Types.Copy();
                }
            }

            Container = container;
            this.ContainerElementSetting = ContainerElementSettings[key];
        }

        #region IInstanceProvider Members

        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            string typeMappingName = null;
            foreach (var item in this.ContainerElementSetting)
            {
                if (item.Type == this._contractType && item.MapTo == instanceContext.Host.Description.ServiceType)
                {
                    typeMappingName = item.Name;
                    break;
                }
            }

            if (typeMappingName == null)
            {
                typeMappingName = Guid.NewGuid().ToString();
                this.Container.RegisterType(this._contractType, instanceContext.Host.Description.ServiceType,typeMappingName);
                this.ContainerElementSetting.Add(new UnityTypeMapping { Type = this._contractType, MapTo = instanceContext.Host.Description.ServiceType, Name = typeMappingName });
            }

            this.Container.Configure<Interception>().SetDefaultInterceptorFor(this._contractType, injector);
            object instance= this.Container.Resolve(this._contractType,typeMappingName);

            return instance;
        }

        private UnityContainerElement GetUnitySettings(string containerName)
        {
            UnityConfigurationSection unitySection = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
            if (unitySection == null)
            {
                throw new ConfigurationErrorsException(string.Format(CultureInfo.CurrentCulture, "Missing Unity configuration section."));
            }

            if (string.IsNullOrEmpty(containerName))
            {
                return unitySection.Containers.Default;
            }
            else
            {
                return unitySection.Containers[containerName];
            }
        }

        public object GetInstance(InstanceContext instanceContext)
        {
            return this.GetInstance(instanceContext, null);
        }

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            IDisposable disposable = instance as IDisposable;
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }

        #endregion
    }
}